package jass.generators;
import jass.engine.*;
import jass.render.*;
import jass.generators.*;
import java.net.*;

/**
   Position  based playback  of audio  date (gramophone  model). Wavfile
   (mono) is indexed  with position of needle on  record.  Every call to
   getBuffer this UG  polls for position of needle  in seconds, and uses
   this with the previous saved value to index a wav file and compute an
   audio buffer for the corresponding segment. Method to obtain position
   of needle is abstract.
   Streams audio off source
   @author Kees van den Doel (kvdoel@cs.ubc.ca) 
*/
public abstract class StreamingAudioGroove extends AudioGroove {

    private float[] tempFloatBuffer = null;
    private byte[] tempByteBuffer = null;

    private StreamingAudioFileBuffer afBuffer;
    
    /**
       For derived classes
       @param bufferSize buffer size
     */
    public StreamingAudioGroove(int bufferSize) {
        super(bufferSize); // this is the internal buffer size
    }
    
    /** Construct Groove from named file.
        @param srate sampling rate in Hertz.
        @param bufferSize bufferSize of this Out
        @param fn Audio file name.
    */
    public StreamingAudioGroove(float srate,int bufferSize, String fn) throws UnsupportedAudioFileFormatException {
        super(bufferSize);
        afBuffer = new StreamingAudioFileBuffer(fn);
        grooveBufferLength = afBuffer.bufsz;
        srateGrooveBuffer = afBuffer.srate;
        this.srate = srate;
        srateRatio = srateGrooveBuffer/srate;
        this.name = fn;
    }

    /** Construct Groove from named URL.
        @param srate sampling rate in Hertz.
        @param bufferSize bufferSize of this Out
        @param url Audio file url name.
    */
    public StreamingAudioGroove(float srate,int bufferSize, URL url) throws UnsupportedAudioFileFormatException {
        super(bufferSize); // this is the internal buffer size
        afBuffer = new StreamingAudioFileBuffer(url);
        grooveBufferLength = afBuffer.bufsz;
        srateGrooveBuffer = afBuffer.srate;
        this.srate = srate;
        srateRatio = srateGrooveBuffer/srate;
        this.name = url.toString();
    }

    /** Get the groove buffer as array, which is not possible. So return null
        @return null
    */
    public float[] getGrooveBuffer() {
        return null;
    }

    /** Compute the next buffer.
     */
    public void computeBuffer() {
        int bufsz = getBufferSize();
        posNeedlePast = posNeedle;
        posNeedle = getPositionOfNeedle();
        
        double ireal; // (fractional) index into grooveBuffer
        // ireal = a + b * k (k integer index in buffer to compute)a 
        double a = posNeedlePast * srateGrooveBuffer;
        double b = (posNeedle*srateGrooveBuffer - a)/(bufsz);
        //System.out.println("p= "+posNeedle+"ppast= "+posNeedlePast);
        
        // determine which segment of the audiogroove we need to access and stream it into temp buffer
        // done rather inelegantly by making a dummy pass through the buffer...
        int i_min = grooveBufferLength;
        int i_max = -1;
        for(int k=0;k<bufsz;k++) {
            ireal = a + b*(k+1);
            if(ireal >= grooveBufferLength -1 || ireal < 0) {
                // no data
            } else {
                int i = (int)ireal; // integer part
                if(i < i_min) {
                    i_min = i;
                }
                if(i+1 > i_max) {
                    i_max = i+1;
                }
            }
        }
        
        // have to pre-load this many float samples:
        int tempFloatBufferLength = i_max - i_min+1;
        int tempByteBufferLength = 2*tempFloatBufferLength;
        if(tempFloatBuffer == null || tempFloatBuffer.length < tempFloatBufferLength) {
            tempFloatBuffer = new float[tempFloatBufferLength];
        }
        if(tempByteBuffer == null || tempByteBuffer.length < tempByteBufferLength) {
            tempByteBuffer = new byte[tempByteBufferLength];
        }
        int offset = 0;
        try {
            
            //afBuffer.randomAccessFile.seek(2*i_min); // double for bytes
            //afBuffer.randomAccessFile.readFully(tempByteBuffer,offset,tempByteBufferLength);
            
            // do seek() emulation: alas is not supported by JavaSound!
            //afBuffer.audioInputStream.reset();
            //afBuffer.audioInputStream.skip(2*i_min); // double for bytes
            afBuffer.audioInputStream.read(tempByteBuffer,offset,tempByteBufferLength);
        } catch(Exception e) {
            System.out.println("seek failed:"+e);
        }
        FormatUtils.byteToFloat(tempFloatBuffer,tempByteBuffer,tempFloatBufferLength);
        // grooveBuffer[i] now corresponds to tempFloatBuffer[i-i_min]
        
        for(int k=0;k<bufsz;k++) {
            ireal = a + b*(k+1);
            if(ireal >= grooveBufferLength -1 || ireal < 0) {
                buf[k] = 0;
            } else {
                int i = (int)ireal; // integer part
                double ifrac = ireal - i; // fractional part
                buf[k] = (float)((1-ifrac)*tempFloatBuffer[i-i_min] + ifrac*tempFloatBuffer[i-i_min+1]);
            }
        }
        //System.out.println(buf[0]);
        
    }

}


